From b8ed3e9ef52bb45ff9a3836e50fd7b90936c2c97 Mon Sep 17 00:00:00 2001 From: Chris Cummins Date: Thu, 28 Mar 2013 12:08:43 +0000 Subject: [PATCH] wayland: Implement animatable cursors Cursor animations are handled on a per-device basis, with GdkWaylandDevice updating the pointer surface for each frame. https://bugzilla.gnome.org/show_bug.cgi?id=696429 --- gdk/wayland/gdkcursor-wayland.c | 54 ++++++++++++++++++++----- gdk/wayland/gdkdevice-wayland.c | 68 +++++++++++++++++++++++++++----- gdk/wayland/gdkprivate-wayland.h | 8 +++- 3 files changed, 108 insertions(+), 22 deletions(-) diff --git a/gdk/wayland/gdkcursor-wayland.c b/gdk/wayland/gdkcursor-wayland.c index 8873813c75..fc51473677 100644 --- a/gdk/wayland/gdkcursor-wayland.c +++ b/gdk/wayland/gdkcursor-wayland.c @@ -182,6 +182,7 @@ gdk_wayland_cursor_get_image (GdkCursor *cursor) struct wl_buffer * _gdk_wayland_cursor_get_buffer (GdkCursor *cursor, + guint image_index, int *x, int *y, int *w, @@ -191,13 +192,25 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor, if (wayland_cursor->wl_cursor) { - *x = wayland_cursor->wl_cursor->images[0]->hotspot_x; - *y = wayland_cursor->wl_cursor->images[0]->hotspot_y; + struct wl_cursor_image *image; - *w = wayland_cursor->wl_cursor->images[0]->width; - *h = wayland_cursor->wl_cursor->images[0]->height; + if (image_index >= wayland_cursor->wl_cursor->image_count) + { + g_warning (G_STRLOC " out of bounds cursor image [%d / %d]", + image_index, + wayland_cursor->wl_cursor->image_count - 1); + image_index = 0; + } - return wl_cursor_image_get_buffer(wayland_cursor->wl_cursor->images[0]); + image = wayland_cursor->wl_cursor->images[image_index]; + + *x = image->hotspot_x; + *y = image->hotspot_y; + + *w = image->width; + *h = image->height; + + return wl_cursor_image_get_buffer (image); } else /* From pixbuf */ { @@ -211,6 +224,32 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor, } } +guint +_gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor, + guint current_image_index, + guint *next_image_delay) +{ + struct wl_cursor *wl_cursor = GDK_WAYLAND_CURSOR (cursor)->wl_cursor; + + if (wl_cursor && wl_cursor->image_count > 1) + { + if (current_image_index >= wl_cursor->image_count) + { + g_warning (G_STRLOC " out of bounds cursor image [%d / %d]", + current_image_index, wl_cursor->image_count - 1); + current_image_index = 0; + } + + /* Return the time to next image */ + if (next_image_delay) + *next_image_delay = wl_cursor->images[current_image_index]->delay; + + return (current_image_index + 1) % wl_cursor->image_count; + } + else + return current_image_index; +} + static void _gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class) { @@ -335,11 +374,6 @@ _gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, if (!set_cursor_from_theme (private, wayland_display->cursor_theme)) return GDK_CURSOR (private); - /* TODO: Do something clever so we can do animated cursors - move the - * wl_pointer_set_cursor to a function here so that we can do the magic to - * iterate through - */ - add_to_cache (wayland_display, private); return GDK_CURSOR (private); diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 4c8caecc2d..74dd5d52f4 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -71,6 +71,9 @@ struct _GdkWaylandDeviceData guint32 repeat_count; GSettings *keyboard_settings; + guint cursor_timeout_id; + guint cursor_image_index; + DataOffer *drag_offer; DataOffer *selection_offer; @@ -144,14 +147,63 @@ gdk_wayland_device_get_state (GdkDevice *device, } } +static void +gdk_wayland_device_stop_window_cursor_animation (GdkWaylandDeviceData *wd) +{ + if (wd->cursor_timeout_id > 0) + { + g_source_remove (wd->cursor_timeout_id); + wd->cursor_timeout_id = 0; + } + wd->cursor_image_index = 0; +} + +static gboolean +gdk_wayland_device_update_window_cursor (GdkWaylandDeviceData *wd) +{ + struct wl_buffer *buffer; + int x, y, w, h; + guint next_image_index, next_image_delay; + + buffer = _gdk_wayland_cursor_get_buffer (wd->cursor, wd->cursor_image_index, + &x, &y, &w, &h); + wl_pointer_set_cursor (wd->wl_pointer, + wd->enter_serial, + wd->pointer_surface, + x, y); + wl_surface_attach (wd->pointer_surface, buffer, 0, 0); + wl_surface_damage (wd->pointer_surface, 0, 0, w, h); + wl_surface_commit (wd->pointer_surface); + + next_image_index = + _gdk_wayland_cursor_get_next_image_index (wd->cursor, + wd->cursor_image_index, + &next_image_delay); + + if (next_image_index != wd->cursor_image_index) + { + guint id; + + /* Queue timeout for next frame */ + id = g_timeout_add (next_image_delay, + (GSourceFunc)gdk_wayland_device_update_window_cursor, + wd); + + wd->cursor_timeout_id = id; + wd->cursor_image_index = next_image_index; + } + else + wd->cursor_timeout_id = 0; + + return FALSE; +} + static void gdk_wayland_device_set_window_cursor (GdkDevice *device, GdkWindow *window, GdkCursor *cursor) { GdkWaylandDeviceData *wd = GDK_WAYLAND_DEVICE(device)->device; - struct wl_buffer *buffer; - int x, y, w, h; /* Setting the cursor to NULL means that we should use the default cursor */ if (!cursor) @@ -164,19 +216,14 @@ gdk_wayland_device_set_window_cursor (GdkDevice *device, if (cursor == wd->cursor) return; + gdk_wayland_device_stop_window_cursor_animation (wd); + if (wd->cursor) g_object_unref (wd->cursor); wd->cursor = g_object_ref (cursor); - buffer = _gdk_wayland_cursor_get_buffer (wd->cursor, &x, &y, &w, &h); - wl_pointer_set_cursor (wd->wl_pointer, - wd->enter_serial, - wd->pointer_surface, - x, y); - wl_surface_attach (wd->pointer_surface, buffer, 0, 0); - wl_surface_damage (wd->pointer_surface, 0, 0, w, h); - wl_surface_commit (wd->pointer_surface); + gdk_wayland_device_update_window_cursor (wd); } static void @@ -622,6 +669,7 @@ pointer_handle_leave (void *data, g_object_unref(device->pointer_focus); if (device->cursor) { + gdk_wayland_device_stop_window_cursor_animation (device); g_object_unref (device->cursor); device->cursor = NULL; } diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index 4dcc80cbfc..8421043506 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -78,10 +78,14 @@ gboolean _gdk_wayland_display_supports_cursor_alpha (GdkDisplay *display); gboolean _gdk_wayland_display_supports_cursor_color (GdkDisplay *display); struct wl_buffer *_gdk_wayland_cursor_get_buffer (GdkCursor *cursor, - int *x, - int *y, + guint image_index, + int *x, + int *y, int *w, int *h); +guint _gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor, + guint current_image_index, + guint *next_image_delay); GdkDragProtocol _gdk_wayland_window_get_drag_protocol (GdkWindow *window, GdkWindow **target); -- 2.30.2